var i = 101987;

// 拖拽对象
var dragObj = {
  // 拖拽时的节点
  current: null,
  // 拖拽时，所依附的目标节点
  target: null,

  // 最后一次可拖拽到目标的目标节点
  lastTarget: null,
  /**
   * 连线的起点和终点
   */
  line: [[0, 0], [0, 0]],
};

var setting = {
  // default setting
  onRemove() { },
  onAdd() { },
  onEdit() { },
  onDragend() { },
  margin: { top: 0, right: 20, bottom: 30, left: 20 },
  originWidth: 900,
  originHeight: 650,
  rectWidth: 160,
  rectHeight: 70,
  horizonRange: 60,
  duration: 750,
  radius: 10,
  fontSize: 16,
  rootNode: {
    x: 0,
    y: 0,
    width: 160,
    height: 70,
  },
};

setting.width = setting.originWidth - setting.margin.left - setting.margin.right;
setting.height = setting.originHeight - setting.margin.top - setting.margin.bottom;

var svg;
var transformParam = {
  k: 1,
  x: 0,
  y: 0,
};
var viewInfo;
// 定义缩放
var zoomListener = d3.zoom()
  .scaleExtent([0.2, 2])
  // .translateExtent([[0, 0], [setting.width, setting.height]])
  // .translateExtent([[setting.originWidth / 2, setting.originHeight / 2], [setting.originWidth / 2, setting.originHeight / 2]])
  .on("zoom", function (e) {
    viewInfo = d3.select('.viewer').node().getBBox();
    svg.attr('transform', transformParam = d3.event.transform);
  });

svg = d3.select("body").append("svg")
  .attr("width", setting.width + setting.margin.right + setting.margin.left)
  .attr("height", setting.height + setting.margin.top + setting.margin.bottom)
  .call(zoomListener)
  // .on('mousedown.zoom', null)
  .append('g')
  .attr('class', 'viewer');

var nodeWrapper = svg.append("g")
  .attr('class', 'node-wrapper')
  .attr("transform", "translate("
  + setting.margin.left + "," + setting.margin.top + ")");

const lineWrapper = svg
  .append('g')
  .attr('class', 'line-wrapper')
  .attr("transform", "translate("
  + setting.margin.left + "," + setting.margin.top + ")");
var treemap = d3.tree()
  .size([setting.height, setting.width])
var root;

fetchData().then(treeData => {

  root = d3.hierarchy(treeData, function (d) { return d.children; });
  root.x0 = setting.height / 2;
  root.y0 = 0;
  root.children.forEach(d => collapse(d));
  root.sort((a, b) => a.data.name.toLowerCase() > b.data.name.toLowerCase());
  update(root);
});

function update(source) {
  var treeData = treemap(root);
  // Compute the new tree layout.
  var nodes = treeData.descendants();
  nodes.forEach(d => d.y = d.depth * (setting.rectWidth + setting.horizonRange));
  // 节点顶部对齐(重新绘制节点数)
  alignTop(root);

  var node = nodeWrapper.selectAll('g.depart')
    // .data(nodes);
    .data(nodes, function (d) {
      return d.id || (d.id = ++i);
      // return d.data.id;
    });
  

  var lineGroup = lineWrapper.selectAll('g.depart-link')
    .data(nodes, function (d) {
      return ++i;
      // return d.data.id;
    });

  // 根节点没有连线
  var lineEnter = lineGroup.enter()
    .filter(d => d.parent).append('g')
    .attr('class', 'depart-link')
    .append('path')
    .attr('d', d => {
      return `M${d.y - setting.radius} ${d.x}`;
    });

  var lineUpdate = lineEnter.merge(lineGroup);
  lineUpdate
    // .attr('transform', `translate(${source.y0}, ${source.x0})`)
    // .attr('transform', d => `translate(${d.y}, ${d.x})`)
    .attr('transform', d => `translate(${0}, 0)`)
    .attr('style', 'opacity: 0')
    // .attr('transform', `translate(0, ${setting.rectHeight / 2})`)
    .transition()
    .duration(setting.duration)
    .attr('d', d => {
      if (d.parent) {
        return `M${d.y - setting.radius} ${d.x}L${d.y + setting.horizonRange / -2} ${d.x}M${d.y + setting.horizonRange / -2} ${d.x}L${d.y + setting.horizonRange / -2} ${d.parent.x}M${d.y + setting.horizonRange / -2} ${d.parent.x}L${d.parent.y + setting.rectWidth} ${d.parent.x}`
      }
    })
    .attr('transform', d => `translate(${0}, ${setting.rectHeight / 2})`)
    .attr('style', 'opacity: 1');
  // .attr('transform', d => `translate(${d.y}, ${d.x})`);

  var lineExit = lineGroup.exit()
    .attr('style', 'opacity: 1')
    .transition()
    .duration(setting.duration / 2)
    .attr('style', 'opacity: 0')
    .remove();

  var isAttach = false;
  var draggingTask = null;
  var isCircleExpanded = false;
  // 节点拖拽
  var draggable = d3.drag()
    .on('start', function(d, nodeInx) {
      dragObj.current = d;
      // d3.select(d).raise();
      d.dx = 0;
      d.dy = 0;
      // d3.event.sourceEvent.stopPropagation();
      draggingTask = setTimeout(() => {
        d3.select(this).classed('dragging', true);
        if (d.children) {
          d._children = d.children;
          d.children = null;
          update(d);
        }
        nodeWrapper.selectAll('.ghost-circle')
          .filter(not(isParent))
          .filter(n => n !== d)
          .filter(n => n !== d.parent)
          .attr('r', 0)
          .transition()
          .duration(150)
          .attr('r', setting.radius * 6)
          .on('end', () => {
            isCircleExpanded = true;
          });
      }, 150);
    })
    .on('drag', function (d) {
      const { x, y } = d3.event;
      d.dx += d3.event.dx;
      d.dy += d3.event.dy;
      d3.select(this)
        .attr('transform',
        `translate(${d.dx}, ${d.dy})`);
      
      if (dragObj.current && dragObj.target) {
        dragObj.line[0] = [
          dragObj.target.y + setting.margin.left,
          dragObj.target.x + setting.margin.top + setting.rectHeight / 2
        ];
        dragObj.line[1] = [
          // (-transformParam.x + d3.event.x) / transformParam.k,
          // (-transformParam.y + d3.event.y) / transformParam.k,
          (-transformParam.x + d3.event.sourceEvent.pageX) / transformParam.k,
          (-transformParam.y + d3.event.sourceEvent.pageY) / transformParam.k,
        ];
        createLink(
          {
            x: dragObj.line[0][0],
            y: dragObj.line[0][1]
          },
          {
            // x: (setting.margin.top + transformParam.x + d3.event.offsetX) * transformParam.k, 
            // y: (setting.margin.left + transformParam. x + d3.event.offsetY) * transformParam.k
            x: dragObj.line[1][0],
            y: dragObj.line[1][1],
          }
        );
      }
    })
    .on('end', function (d) {
      d3.select(this).classed('dragging', false);
      svg.selectAll('.dragging-target').classed('dragging-target', false);
      
      clearTimeout(draggingTask);

      if (isCircleExpanded) {
        nodeWrapper.selectAll('.ghost-circle')
          .filter(n => n !== d)
          .filter(n => n !== d.parent)
          .attr('r', setting.radius * 6)
          .transition()
          .duration(setting.duration)
          .attr('r', 0);
      }
      isCircleExpanded = false;
      if(dragObj.current && dragObj.target && not(isParent)(d)) {
        // 拖拽操作完成，调整节点结构
        // Step.1 删除旧节点
        var index = dragObj.current.parent.children.indexOf(d);
        if (index > -1) {
          dragObj.current.parent.children.splice(index, 1);
          if (dragObj.current.parent.children.length === 0) {
            dragObj.current.parent.children = null;
          }
        }

        // Step.2 新增至新节点下        
        dragObj.current.parent = dragObj.target;
        dragObj.current.descendants().forEach(setDepth);
        if (dragObj.target.children) {
          dragObj.target.children.push(dragObj.current);
        } else if (dragObj.target._children) {
          dragObj.target.children = dragObj.target._children;
          dragObj.target._children = null;
          dragObj.target.children.push(dragObj.current);
        } else {
          dragObj.target.children = [dragObj.current];
        }
        d3.select(this)
          .transition()
          .duration(setting.duration)
          .attr('transform', `translate(0, 0)`);
        d.dx = 0;
        d.dy = 0;

        // Step.3 刷新
        var t = d3.zoomIdentity.translate(0, 0).scale(1)
        update(root);
        
      } else {
        dragObj.target = null;
        d3.select(this)
          .transition()
          .duration(setting.duration)
          .attr('transform', `translate(0, 0)`);
        d.dx = 0;
        d.dy = 0;
      }
      
      removeLink();
      // 将拖拽的节点坐标变为0
      
      dragObj.current = null;
    });

  // debugger;
  var nodeEnter = node.enter()
    .append('g')
    .attr('transform', function (d) {
      return 'translate(' + source.y0 + ',' + source.x0 + ')';
    })
    .attr('class', 'depart')
    .on('click', click);
    
  const nodeEnterWrapper = nodeEnter
    .append('g')
    .attr('class', 'depart-inner')
    .call(draggable)
    .on('mouseenter', function(d) {
      
      if(dragObj.current && d !== dragObj.current && not(isParent)(d) && dragObj.current.parent !== d) {
        // 拖拽至目标节点上
        dragObj.lastTarget = d;
        dragObj.target = d;
        d3.select(this).classed('dragging-target', true);
      }
    })
    .on('mouseleave', function(d) {
      d3.select(this).classed('dragging-target', false);
      dragObj.target = null;
      
      removeLink();
    })

  nodeEnterWrapper
    .append('rect')
    .attr('class', 'node-border')
    .attr('width', node => (node.parent ? setting.rectWidth : setting.rootNode.width))
    .attr('height', node => (node.parent ? setting.rectHeight : setting.rootNode.height))
  // .attr('transform', d => `translate(${d.y}, ${d.x})`)
  
  // 绘制圆
  nodeEnterWrapper.append('circle')
    .attr('class', 'node')
    .attr('r', 1e-6)
    .style("fill", function (d) {
      return d._children ? "lightsteelblue" : "#fff";
    });
  nodeEnterWrapper.append('circle')
    .attr('class', 'ghost-circle');
    // 增加文本
  nodeEnterWrapper.append('text')
    .attr('class', 'depart-name');
  // UPDATE
  var nodeUpdate = nodeEnter.merge(node);
  // nodeUpdate.transition()
  //   .duration(setting.duration)
  //   .attr('transform', d => `translate(${d.y}, ${d.x + setting.height / 2})`);


  // Update the node attributes and style
  nodeUpdate.select('circle.node')
    .attr('r', setting.radius)
    .style("fill", function (d) {
      return d._children ? "lightsteelblue" : "#fff";
    })
    .attr('transform', `translate(0, ${setting.rectHeight / 2})`)
    .attr('cursor', 'pointer');
  nodeUpdate.select('circle.ghost-circle')
    .attr('r', 0)
    .attr('transform', `translate(0, ${setting.rectHeight / 2})`);
  nodeUpdate.select('text.depart-name')
    .attr('transform', `translate(${setting.rectWidth - 5}, ${(setting.rectHeight + setting.fontSize) / 2})`)
    .attr('style', `font-size: ${setting.fontSize + 'px'}`)
    .text(d => {
      return d.data.name || 'unknow';
    });
    
  var nodeExit = node.exit()
    .attr('style', 'opacity: 1')
    .transition()
    .duration(setting.duration)
    .attr('style', 'opacity: 0')
    .attr("transform", function (d) {
      return "translate(" + source.y + "," + source.x + ")";
    })
    .remove();
  nodes.forEach(function (d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
  nodes.forEach(d => {
    if (!!d.parent) {
      // drawLine(d.parent, d, lineWrapper, setting);
    }
  })
  nodeUpdate
    .transition()
    .duration(setting.duration)
    .attr("transform", function (d) {
      return "translate(" + d.y + "," + d.x + ")";
    })
    .on('end', () => {
    });
  setTimeout(() => {
    centerNode();      
  }, setting.duration);
};
function click(d) {
  if (d.children || d._children) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    update(d);
  }
};
function collapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(d => collapse(d));
    d.children = null;
  }
};

function alignTop(node, key = 'x') {
  if (node.children && node.children.length > 0) {
    node[key] = alignTop(node.children[0]);
    return node[key];
  }
  return node[key];
}
/**
 * 对节点的子节点深度赋值为当前节点深度+1
 * @param {object} d 
 */
function setDepth(d) {
  d.depth = !d.parent ? 0 : d.parent.depth + 1;
  if (d.children) {
    d.children.forEach(setDepth);
  } else if (d._children) {
    d._children.forEach(setDepth);
  }
}

function isParent(nodeA) {
  var result = false;
  do {
    if (nodeA.parent == dragObj.current) {
      result = true;
      break;
    }
  } while (nodeA = nodeA.parent)
  return result;
}
/**
 * 对函数执行结果取反
 */
function not(fn) {
  return function () {
    return !fn.apply(null, arguments);
  }
}
/**
 * 获取数据，返回Promise
 */
function fetchData() {
  return new Promise((resolve, reject) => {
    try {
      d3.json('./json/depart.v4.json', data => {
        resolve(data);
      });
    } catch (err) {
      console.error(err);
      reject(err);
    }
  });
}
// 画布居中
function centerNode() {
  var canvasWidth = setting.width / transformParam.k;
  var canvasHeight = setting.height / transformParam.k;
  viewInfo = d3.select('.viewer').node().getBBox();
  var viewWidth = viewInfo.width / transformParam.k;
  var viewHeight = viewInfo.height / transformParam.k;
  
  var t;
  var scale = transformParam.k;
  if (viewInfo.width > setting.originWidth / transformParam.k) {
    scale = setting.originWidth / (viewInfo.width + setting.margin.left);
  } else {
    // 没有超出
    if (scale < 1) {
      scale = setting.originWidth / (viewInfo.width + setting.margin.left);
      scale = Math.min(1, scale);
    }
  }
  t = d3.zoomIdentity.translate(0, 0).scale(scale);
  
  d3.select(".viewer")
    .transition()
  	.duration(500)
  	.attr("transform", `translate(${t.x},${t.y})scale(${t.k})`)
    .on('end', () => {
      transformParam.x = transformParam.y = 0;
      transformParam.k = t.k;
    });
};
/**
 * 
 * @param {{x: number; y: number}} src 起点坐标
 * @param {{x: number; y: number}} tar 重点坐标
 */
function createLink(src, tar) {
  var shortLine = svg.selectAll('path.short-line');
  if (shortLine.size() === 0) {
    svg.append('path')
      .classed('short-line', true);
  }
  shortLine
    .attr('d', d => `M ${src.x}, ${src.y} L ${tar.x} ${tar.y}`);
}
function removeLink() {
  svg.selectAll('path.short-line')
    .attr('style', 'opacity:1')
    .attr('d', `M ${dragObj.line[0][0]}, ${dragObj.line[0][1]} L ${dragObj.line[1][0]}, ${dragObj.line[1][1]}`)
    .transition()
    .duration(300)
    .attr('style', 'opacity:1')
    .attr('d', `M ${dragObj.line[0][0]}, ${dragObj.line[0][1]} L ${dragObj.line[0][0]}, ${dragObj.line[0][1]}`)
    .remove();
}
